#pragma once

#include "BasicTypes.h"
#include "Definitions.h"

namespace MOE
{
	namespace Core
	{
		class ClassMeta;
		class Field;

		// Helper functions to read/write basic type values.
		// All functions return true if the read/write operation succeeds, and false if it's not possible (e.g. source/target empty, type incompatible, etc.).
		namespace BasicTypeHelpers
		{
			// Template function to read values of type T from a basic type object.
			// NOT implemented by default. Only specialized functions are implemented.
			template <typename T>
			bool Read(const ClassMeta* classMeta, tUntypedConstRef source, T& outValue)
			{
				static_assert("Output type not supported!");
			}

			template <> bool Read<int8>(const ClassMeta* classMeta, tUntypedConstRef source, int8& outValue);
			template <> bool Read<uint8>(const ClassMeta* classMeta, tUntypedConstRef source, uint8& outValue);
			template <> bool Read<int16>(const ClassMeta* classMeta, tUntypedConstRef source, int16& outValue);
			template <> bool Read<uint16>(const ClassMeta* classMeta, tUntypedConstRef source, uint16& outValue);
			template <> bool Read<int32>(const ClassMeta* classMeta, tUntypedConstRef source, int32& outValue);
			template <> bool Read<uint32>(const ClassMeta* classMeta, tUntypedConstRef source, uint32& outValue);
			template <> bool Read<int64>(const ClassMeta* classMeta, tUntypedConstRef source, int64& outValue);
			template <> bool Read<uint64>(const ClassMeta* classMeta, tUntypedConstRef source, uint64& outValue);

			// Template function to write values of type T to a basic type object.
			// NOT implemented by default. Only specialized functions are implemented.
			template <typename T>
			bool Write(const ClassMeta* classMeta, const T& value, tUntypedRef target)
			{
				static_assert("Input type not supported!");
			}

			template <> bool Write<int8>(const ClassMeta* classMeta, const int8& value, tUntypedRef target);
			template <> bool Write<uint8>(const ClassMeta* classMeta, const uint8& value, tUntypedRef target);
			template <> bool Write<int16>(const ClassMeta* classMeta, const int16& value, tUntypedRef target);
			template <> bool Write<uint16>(const ClassMeta* classMeta, const uint16& value, tUntypedRef target);
			template <> bool Write<int32>(const ClassMeta* classMeta, const int32& value, tUntypedRef target);
			template <> bool Write<uint32>(const ClassMeta* classMeta, const uint32& value, tUntypedRef target);
			template <> bool Write<int64>(const ClassMeta* classMeta, const int64& value, tUntypedRef target);
			template <> bool Write<uint64>(const ClassMeta* classMeta, const uint64& value, tUntypedRef target);

			// Template function to read values of type T from a field on an object.
			// NOT implemented by default. Only specialized functions are implemented.
			template <typename T>
			bool Read(const Field* field, tUntypedConstRef sourceObj, T& outValue)
			{
				static_assert("Output type not supported!");
			}

			template <> bool Read<int8>(const Field* field, tUntypedConstRef sourceObj, int8& outValue);
			template <> bool Read<uint8>(const Field* field, tUntypedConstRef sourceObj, uint8& outValue);
			template <> bool Read<int16>(const Field* field, tUntypedConstRef sourceObj, int16& outValue);
			template <> bool Read<uint16>(const Field* field, tUntypedConstRef sourceObj, uint16& outValue);
			template <> bool Read<int32>(const Field* field, tUntypedConstRef sourceObj, int32& outValue);
			template <> bool Read<uint32>(const Field* field, tUntypedConstRef sourceObj, uint32& outValue);
			template <> bool Read<int64>(const Field* field, tUntypedConstRef sourceObj, int64& outValue);
			template <> bool Read<uint64>(const Field* field, tUntypedConstRef sourceObj, uint64& outValue);

			// Template function to read values of type T to a field on an object.
			// NOT implemented by default. Only specialized functions are implemented.
			template <typename T>
			bool Write(const Field* field, const T& value, tUntypedRef targetObj)
			{
				static_assert("Input type not supported!");
			}

			template <> bool Write<int8>(const Field* field, const int8& value, tUntypedRef targetObj);
			template <> bool Write<uint8>(const Field* field, const uint8& value, tUntypedRef targetObj);
			template <> bool Write<int16>(const Field* field, const int16& value, tUntypedRef targetObj);
			template <> bool Write<uint16>(const Field* field, const uint16& value, tUntypedRef targetObj);
			template <> bool Write<int32>(const Field* field, const int32& value, tUntypedRef targetObj);
			template <> bool Write<uint32>(const Field* field, const uint32& value, tUntypedRef targetObj);
			template <> bool Write<int64>(const Field* field, const int64& value, tUntypedRef targetObj);
			template <> bool Write<uint64>(const Field* field, const uint64& value, tUntypedRef targetObj);

			// Template helper class to check if a type is a supported basic type.
			template <class T>
			class IsSupportedBasicType
			{
			public:
				static const bool Value = false;
			};

			template <>
			class IsSupportedBasicType<int8>
			{
			public:
				static const bool Value = true;
			};

			template <>
			class IsSupportedBasicType<uint8>
			{
			public:
				static const bool Value = true;
			};

			template <>
			class IsSupportedBasicType<int16>
			{
			public:
				static const bool Value = true;
			};

			template <>
			class IsSupportedBasicType<uint16>
			{
			public:
				static const bool Value = true;
			};

			template <>
			class IsSupportedBasicType<int32>
			{
			public:
				static const bool Value = true;
			};

			template <>
			class IsSupportedBasicType<uint32>
			{
			public:
				static const bool Value = true;
			};

			template <>
			class IsSupportedBasicType<int64>
			{
			public:
				static const bool Value = true;
			};

			template <>
			class IsSupportedBasicType<uint64>
			{
			public:
				static const bool Value = true;
			};

			template <>
			class IsSupportedBasicType<float32>
			{
			public:
				static const bool Value = true;
			};

			template <>
			class IsSupportedBasicType<float64>
			{
			public:
				static const bool Value = true;
			};
		}
	}
}
